// Copyright (c) 2009, Anton Korenyushkin
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the names of contributors may be
//       used to endorse or promote products derived from this software
//       without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

(function ()
{
  return TestCase.subclass(
    {
      setUp: function () {
        init();

        this._client = new ak.TestClient(
          ['user1', 'user2', 'user3'],
          {
            app1: {
              admin: 'user1',
              developers: ['user3']
            },
            app2: {
              admin: 'user2',
              developers: ['user1']
            },
            app3: {
              admin: 'user1'
            },
            app4: {
              admin: 'user2'
            }
          });
        this._client.login('user1');

        [
          {
            issuer: 'app1',
            data: {
              level: 10,
              message: 'debug'
            }
          },
          {
            issuer: 'app1',
            user: 'user2',
            data: {
              level: 20,
              message: 'info',
              spot: {name: 'spot1', owner: 'user1'}
            }
          },
          {
            issuer: 'app2',
            user: 'user1',
            data: {
              level:30,
              message: 'warning',
              spot: {name: 'spot3', owner: 'user2'}
            }
          },
          {
            issuer: 'app1',
            user: 'user1',
            data: {
              level: 40,
              message: 'error',
              spot: {name: 'spot2', owner: 'user1'}
            }
          },
          {
            issuer: 'app1',
            data: {
              level: 50,
              message: 'critical',
              spot: {name: 'spot1', owner: 'user1'}
            }
          },
          {
            issuer: 'app1',
            data: {
              level: 20,
              message: 'another info'
            }
          }
        ].forEach(
          function (request) {
            request.method = 'post';
            request.data = JSON.stringify(request.data);
            assertSame(this._client.request(request).status, http.CREATED);
          },
          this);
      },

      tearDown: function () {
        init();
      },

      _get: function (path) {
        return this._client.get({path: path});
      },

      testMainController: function () {
        assertEqual(items(this._get('').context),
                    [['user', 'user1'],
                     ['adminedApps', ['app1', 'app3']],
                     ['developedApps', ['app2']]]);
        assertSame(this._client.get({path: '', user: undefined}).status,
                   http.FOUND);
      },

      testAppController: function () {
        assertSame(this._get('apps/no_such_app/').status,
                   http.NOT_FOUND);
        assertSame(this._get('apps/app4/').status,
                   http.FORBIDDEN);
        var context = this._get('apps/app1/').context;
        assertSame(context.descr, this._client.apps.app1);
        assertSame(context.releasePlace.count, 2);
        assertSame(this._get('apps/app2/').context.releasePlace, undefined);
      },

      testAppControllerReleasePage: function () {
        var context = this._get('apps/app1/release/').context;
        assertSame(context.place.count, 2);
        assertEqual(context.records.map(
                      function (record) {
                        return [record.level, record.message];
                      }),
                    [[10, 'debug'], [20, 'another info']]);
      },

      testOwnerController: function () {
        var context = this._get('apps/app1/owners/user1/').context;
        assertSame(context.releasePlace.count, 2);
        assertEqual(context.spotPlaces.map(attrGetter('spot')),
                    ['spot1', 'spot2']);
        context = this._get('apps/app2/owners/user2/').context;
        assertSame(context.owner, 'user2');
        assertSame(context.releasePlace, undefined);
        var response = this._get('apps/app1/owners/user3/');
        assertSame(response.status, http.OK);
        assertSame(response.context.releasePlace, undefined);
        assertSame(this._get('apps/app3/owners/user2/').status, http.NOT_FOUND);
      },

      testSpotController: function () {
        assertSame(
          this._get('apps/app1/owners/user1/spots/no_such_spot/').status,
          http.NOT_FOUND);
        assertEqual(
          (this._get('apps/app1/owners/user1/spots/spot1/').context
           .records.map(attrGetter('message'))),
          ['info', 'critical']);
      },

      testMain: function () {
        assertSame(this._client.get({issuer: 'app1'}).status,
                   http.METHOD_NOT_ALLOWED);
        assertSame(this._client.post({issuer: 'app1', path: 'path'}).status,
                   http.NOT_FOUND);
        assertSame(this._client.post({issuer: 'app1', data: 'non-JSON'}).status,
                   http.BAD_REQUEST);
        assertSame(this._client.post({issuer: 'app1',
                                      data: '{level: "non-int"}'}).content,
                   'Level must be integer');
      },

      testLibrary: function () {
        var data;
        var aspect = weave(InsteadOf, ak, 'request',
                           function (app, request) {
                             assertSame(app, 'log');
                             assertSame(request.method, 'post');
                             data = JSON.parse(request.data);
                             return {status: http.CREATED};
                           });
        use('log');
        assertThrow(UsageError, log.log);
        var oldSpot = ak.app.spot;
        ak.app.spot = 42;
        log.debug('hi');
        assertEqual(items(data),
                    [['level', 10], ['message', 'hi'], ['spot', 42]]);
        delete ak.app.spot;
        log.log(15, 'hello');
        assertEqual(items(data),
                    [['level', 15], ['message', 'hello']]);

        log.debug();
        assertSame(data.level, log.DEBUG);
        log.info();
        assertSame(data.level, log.INFO);
        log.warning();
        assertSame(data.level, log.WARNING);
        log.error();
        assertSame(data.level, log.ERROR);
        log.critical();
        assertSame(data.level, log.CRITICAL);

        ak.app.spot = oldSpot;
        aspect.unweave();
      }
    }).loadSuite();

})();
